using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CollabApp.Domain.Entities;

/// <summary>
/// 排行榜实体类 - 存储各种类型的玩家排名数据
/// 支持不同时间周期和排名类型的排行榜统计
/// </summary>
[Table("rankings")]
public class Ranking : BaseEntity
{
    /// <summary>
    /// 用户ID - 排行榜中的用户标识
    /// </summary>
    [Required]
    [Column("user_id")]
    public Guid UserId { get; private set; }

    /// <summary>
    /// 排行榜类型 - 排名的计算依据
    /// 例如：总积分、周积分、月积分、胜率等
    /// </summary>
    [Required]
    [Column("ranking_type")]
    public RankingType RankingType { get; private set; }

    /// <summary>
    /// 当前排名 - 用户在该排行榜中的位次
    /// 数值越小排名越高，1为第一名
    /// </summary>
    [Column("current_rank")]
    public int CurrentRank { get; private set; }

    /// <summary>
    /// 分数 - 用于排名计算的分数值
    /// 具体含义根据排行榜类型而定
    /// </summary>
    [Column("score")]
    public int Score { get; private set; }

    /// <summary>
    /// 统计周期开始时间 - 该排行榜统计的起始时间
    /// </summary>
    [Column("period_start")]
    public DateTime PeriodStart { get; private set; }

    /// <summary>
    /// 统计周期结束时间 - 该排行榜统计的结束时间
    /// </summary>
    [Column("period_end")]
    public DateTime PeriodEnd { get; private set; }

    // ============ 构造函数 ============

    /// <summary>
    /// 无参构造函数 - EF Core 必需
    /// </summary>
    private Ranking() { }

    /// <summary>
    /// 私有构造函数 - 仅限工厂方法调用
    /// </summary>
    /// <param name="userId">用户ID</param>
    /// <param name="rankingType">排行榜类型</param>
    /// <param name="currentRank">当前排名</param>
    /// <param name="score">分数</param>
    /// <param name="periodStart">统计周期开始时间</param>
    /// <param name="periodEnd">统计周期结束时间</param>
    private Ranking(Guid userId, RankingType rankingType, int currentRank, int score, 
                   DateTime periodStart, DateTime periodEnd)
    {
        UserId = userId;
        RankingType = rankingType;
        CurrentRank = currentRank;
        Score = score;
        PeriodStart = periodStart;
        PeriodEnd = periodEnd;
    }

    // ============ 导航属性 ============
    
    /// <summary>
    /// 导航属性 - 关联的用户信息
    /// </summary>
    [ForeignKey("UserId")]
    public virtual Auth.User User { get; set; } = null!;

    // ============ 工厂方法 ============

    /// <summary>
    /// 创建排行榜记录 - 工厂方法
    /// </summary>
    /// <param name="userId">用户ID</param>
    /// <param name="rankingType">排行榜类型</param>
    /// <param name="currentRank">当前排名</param>
    /// <param name="score">分数</param>
    /// <param name="periodStart">统计周期开始时间</param>
    /// <param name="periodEnd">统计周期结束时间</param>
    /// <returns>新的排行榜记录实例</returns>
    public static Ranking CreateRanking(Guid userId, RankingType rankingType, int currentRank, 
                                         int score, DateTime periodStart, DateTime periodEnd)
    {
        if (userId == Guid.Empty)
            throw new ArgumentException("用户ID不能为空", nameof(userId));
        if (currentRank <= 0)
            throw new ArgumentException("排名必须大于0", nameof(currentRank));
        if (score < 0)
            throw new ArgumentException("分数不能为负数", nameof(score));
        if (periodStart >= periodEnd)
            throw new ArgumentException("统计周期开始时间必须早于结束时间");

        return new Ranking(userId, rankingType, currentRank, score, periodStart, periodEnd);
    }

    /// <summary>
    /// 创建当前周期排行榜记录 - 工厂方法
    /// </summary>
    /// <param name="userId">用户ID</param>
    /// <param name="rankingType">排行榜类型</param>
    /// <param name="currentRank">当前排名</param>
    /// <param name="score">分数</param>
    /// <returns>新的排行榜记录实例</returns>
    public static Ranking CreateCurrentPeriodRanking(Guid userId, RankingType rankingType, 
                                                      int currentRank, int score)
    {
        var (periodStart, periodEnd) = GetCurrentPeriod(rankingType);
        return CreateRanking(userId, rankingType, currentRank, score, periodStart, periodEnd);
    }

    // ============ 业务方法 ============

    /// <summary>
    /// 更新排名和分数
    /// </summary>
    /// <param name="newRank">新排名</param>
    /// <param name="newScore">新分数</param>
    public void UpdateRanking(int newRank, int newScore)
    {
        if (newRank <= 0)
            throw new ArgumentException("排名必须大于0", nameof(newRank));
        if (newScore < 0)
            throw new ArgumentException("分数不能为负数", nameof(newScore));

        CurrentRank = newRank;
        Score = newScore;
    }

    /// <summary>
    /// 检查排行榜是否在当前统计周期内
    /// </summary>
    /// <returns>是否为当前周期</returns>
    public bool IsCurrentPeriod()
    {
        var now = DateTime.UtcNow;
        return now >= PeriodStart && now <= PeriodEnd;
    }

    /// <summary>
    /// 获取排行榜类型显示名称
    /// </summary>
    /// <returns>类型名称</returns>
    public string GetRankingTypeName()
    {
        return RankingType switch
        {
            RankingType.TotalScore => "总积分排行榜",
            RankingType.WeeklyScore => "周积分排行榜",
            RankingType.MonthlyScore => "月积分排行榜",
            RankingType.WinRate => "胜率排行榜",
            RankingType.Activity => "活跃度排行榜",
            _ => "未知排行榜"
        };
    }

    /// <summary>
    /// 获取指定排行榜类型的当前统计周期
    /// </summary>
    /// <param name="rankingType">排行榜类型</param>
    /// <returns>统计周期的开始和结束时间</returns>
    private static (DateTime Start, DateTime End) GetCurrentPeriod(RankingType rankingType)
    {
        var now = DateTime.UtcNow;
        
        return rankingType switch
        {
            RankingType.WeeklyScore => GetWeeklyPeriod(now),
            RankingType.MonthlyScore => GetMonthlyPeriod(now),
            RankingType.TotalScore or RankingType.WinRate or RankingType.Activity => 
                (DateTime.MinValue, DateTime.MaxValue),
            _ => throw new ArgumentException($"不支持的排行榜类型: {rankingType}")
        };
    }

    /// <summary>
    /// 获取周排行榜的统计周期（周一到周日）
    /// </summary>
    /// <param name="date">基准日期</param>
    /// <returns>周期开始和结束时间</returns>
    private static (DateTime Start, DateTime End) GetWeeklyPeriod(DateTime date)
    {
        var dayOfWeek = (int)date.DayOfWeek;
        var monday = date.AddDays(-(dayOfWeek == 0 ? 6 : dayOfWeek - 1)).Date;
        var sunday = monday.AddDays(6).Date.AddDays(1).AddTicks(-1);
        
        return (monday, sunday);
    }

    /// <summary>
    /// 获取月排行榜的统计周期
    /// </summary>
    /// <param name="date">基准日期</param>
    /// <returns>周期开始和结束时间</returns>
    private static (DateTime Start, DateTime End) GetMonthlyPeriod(DateTime date)
    {
        var firstDay = new DateTime(date.Year, date.Month, 1);
        var lastDay = firstDay.AddMonths(1).AddTicks(-1);
        
        return (firstDay, lastDay);
    }
}

/// <summary>
/// 排行榜类型枚举 - 定义不同的排名计算方式
/// </summary>
public enum RankingType
{
    /// <summary>
    /// 总积分排行榜 - 基于用户历史总积分
    /// </summary>
    TotalScore,
    
    /// <summary>
    /// 周积分排行榜 - 基于当周获得的积分
    /// </summary>
    WeeklyScore,
    
    /// <summary>
    /// 月积分排行榜 - 基于当月获得的积分
    /// </summary>
    MonthlyScore,
    
    /// <summary>
    /// 胜率排行榜 - 基于游戏胜率统计
    /// </summary>
    WinRate,
    
    /// <summary>
    /// 活跃度排行榜 - 基于游戏参与度
    /// </summary>
    Activity
}
